package com.dhms.vostok1.service.ble

import android.annotation.SuppressLint
import android.bluetooth.BluetoothDevice
import android.content.Context
import android.content.Intent
import android.util.Log
import com.bjzc.blemanager.BleManager
import com.bjzc.blemanager.BluetoothLeService
import com.dhms.vostok1.data.BLEDevice
import com.dhms.vostok1.data.BLEDeviceType
import com.dhms.vostok1.data.Feature
import com.dhms.vostok1.utils.Utils
import com.dhms.vostok1.utils.toFormatDate
import java.util.*
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.locks.ReentrantLock
import kotlin.experimental.and
import kotlin.reflect.full.declaredMembers
import kotlin.reflect.jvm.isAccessible

@Suppress("unused")
@SuppressLint("MissingPermission")
class ZhengChaoBleServiceImp private constructor(context: Context,) : BleService(context) {
    private var ble: BleManager = BleManager.getInstance()
    private lateinit var bleServiceCallback: BleServiceCallback
    private var lastCmd: String? = null
    private var vibDataFactor: FloatArray? = null
    private lateinit var vibDataTmp: ByteArray
    private var isHighFrequencyCollection: Boolean? = null
    private var sample: Long? = null
    private var sampleRate: Long? = null
    private var lastRVEL: Float? = null

    private val singleRunnerLock = ReentrantLock()

    /** Wait queue for waiting takes  */
    private val notExecuting = singleRunnerLock.newCondition()
    private var spcTaskQueue = LinkedBlockingQueue<String>()

    private inner class TaskRunner : Thread() {
        override fun run() {
            val taskLocker: ReentrantLock = singleRunnerLock
            while (true) {
                Log.d(TAG, "start taskRunner")
                if (currentThread().isInterrupted) {
                    Log.d(TAG, "exit taskRunner")
                    return
                }
                try {
                    taskLocker.lockInterruptibly()
                    if (currentThread().isInterrupted) {
                        return
                    }
                    var t: String?
                    try {
                        t = spcTaskQueue.take()
                    } catch (e: InterruptedException) {
                        if (taskLocker.isHeldByCurrentThread) {
                            taskLocker.unlock()
                        }
                        return
                    }

                    Log.d(TAG, "take one task $t")
                    if (currentThread().isInterrupted) currentThread().interrupt()
                    lastCmd = t

                    when (lastCmd) {
                        CMD_BATTERY -> {
                            ble.write(CMD_BATTERY, false)
                            Log.d(
                                TAG,
                                "${Thread.currentThread().id} - 请求点检仪电池信息。 cmd: ${CMD_BATTERY}, ishex: false"
                            )
                        }
                        CMD_TEMPERATURE -> {
                            ble.write(CMD_TEMPERATURE, true)
                            Log.d(TAG, "请求点检仪测量温度。 cmd: ${CMD_TEMPERATURE}, ishex: true")
                        }
                        CMD_VVEL -> {
                            ble.write(CMD_VVEL, true)
                            Log.d(TAG, "请求点检仪测量速度。 cmd: ${CMD_VVEL}, ishex: true")
                        }
                        CMD_VAEL -> {
                            ble.write(CMD_VAEL, true)
                            Log.d(TAG, "请求点检仪测量加速度。 cmd: ${CMD_VAEL}, ishex: true")
                        }
                        CMD_FACTOR_TABLE -> {
                            ble.write(CMD_FACTOR_TABLE, false)
                            Log.d(TAG, "请求读取点检仪频谱系数表。 cmd: ${CMD_FACTOR_TABLE}, ishex: true")
                        }
                        CMD_VLB_BLOCK -> {
                            val freqTag = if (isHighFrequencyCollection == true) "01" else "02"
                            val vibDataCmd = String.format(
                                "0e%s%s%s02",
                                COLLECTION_PARAM_MAP_SAMPLE_RATE[sampleRate],
                                COLLECTION_PARAM_MAP_SAMPLE[sample],
                                freqTag
                            )
                            ble.write(vibDataCmd, true)
                            Log.d(TAG, "请求点检仪振动采集数据。 cmd: ${vibDataCmd}, ishex: true")
                        }
                    }

                    Log.d(TAG, "waiting task ($t) result ...")
                    try {
                        notExecuting.await()
                    } catch (e: InterruptedException) {
                        if (taskLocker.isHeldByCurrentThread) {
                            taskLocker.unlock()
                        }
                    }
                } catch (e: InterruptedException) {
                    // ignore the thread interrupted error
                } finally {
                    if (taskLocker.isHeldByCurrentThread) {
                        taskLocker.unlock()
                    }
                }
            }
        }
    }

    private var taskConsumer: Thread? = null

    init {
        ble.init(context.applicationContext)
        ble.setOnReceiveListener(ZuoAnBleDeviceMessageReceivedListener())
    }

    override fun connectDevice(device: BluetoothDevice) {
        Log.d(TAG, "请求传感器连接...")
        try {
            taskConsumer?.interrupt()
        } finally {
            ble.connect(device.address, device.name)
            taskConsumer = TaskRunner()
            taskConsumer!!.start()
        }
    }

    override fun closeConnect() {
        try {
            taskConsumer?.interrupt()
            taskConsumer = null
            lastCmd = null
        } catch (ex: Exception) {
            // ignore the error if stop task failed
        }
        val bleService = ble::class.declaredMembers.find {
            it.name == "mBluetoothLeService"
        }
        bleService?.isAccessible = true
        (bleService?.call() as BluetoothLeService?)?.disconnect()
    }

    override fun upgrade(
        device: BluetoothDevice,
        fileName: String,
        versionNum: Int,
        firmware: ByteArray
    ) {
        // do nothing
    }

    override fun requestPowerLevel() {
        spcTaskQueue.put(CMD_BATTERY)
    }

    override fun requestDeviceInfo(device: BluetoothDevice) {
        val deviceInfo = BLEDevice(
            device,
            BLEDeviceType.ZHENG_CHAO_SPC_DEVICE,
            Utils.parseProductId(device.address),
            "GD3",
            device.address,
            feature = Feature(listOf("TMPR", "VACC", "BVLT")),
            null,
            null,
            null,
            power = null
        )

        bleServiceCallback.onDeviceInfoDataReceived(deviceInfo)
    }

    override fun requestTemperature() {
        spcTaskQueue.put(CMD_TEMPERATURE)
    }

    override fun requestVibAnalysis() {
        spcTaskQueue.put(CMD_VVEL)
        spcTaskQueue.put(CMD_VAEL)
    }

    override fun requestVibData(sample: Long, sampleRate: Long, speed: Long) {
        vibDataTmp = byteArrayOf()
        if (vibDataFactor == null) {
            spcTaskQueue.put(CMD_FACTOR_TABLE)
        }
//        采集时域谱。采集命令解释如下
//        cmd="0e"+AA+BB+CC+DD;
//        AA是采样频率。1028hz是"01"，5120hz是"02"，12800hz是"03"，25600hz是"04"，51200hz是"05"
//        BB是采样长度。1024点是"01"，2048点是"02"，4096点是"03"，8192点是"04"，16384点是"05"
//        CC是高低频:高频是"01"  低频是“02”
//        DD是振动类型:加速度是"02" ，速度“01”， 位移“04”
//        例如：加速度低频采样频率5120hz采样点数4096点，cmd="0e02030202"，关于振动高频、低频的说明：
//        测量目标设备转速低于20000转以下用低频，高于用高频。
        isHighFrequencyCollection = speed > 20000
        this.sample = sample
        this.sampleRate = sampleRate
        spcTaskQueue.put(CMD_VLB_BLOCK)
    }

    companion object {
        private const val TAG: String = "ZuoAnBleServiceImp"

        private const val CMD_BATTERY = "BV"
        private const val CMD_TEMPERATURE = "0B0B6300"
        private const val CMD_VAEL = "1F11"
        private const val CMD_VVEL = "1F12"
        private const val CMD_FACTOR_TABLE = "RK"
        private const val CMD_VLB_BLOCK = "CMD_VLB_BLOCK"
        private val COLLECTION_PARAM_MAP_SAMPLE: Map<Long, String> = mapOf(
            Pair(1024, "01"),
            Pair(2048, "02"),
            Pair(4096, "03"),
            Pair(8192, "04"),
            Pair(16384, "05")
        )
        private val COLLECTION_PARAM_MAP_SAMPLE_RATE: Map<Long, String> = mapOf(
            Pair(5120, "02"),
            Pair(1028, "01"),
            Pair(12800, "03"),
            Pair(25600, "04"),
            Pair(51200, "05")
        )

        @SuppressLint("StaticFieldLeak")
        private var instance: ZhengChaoBleServiceImp? = null

        @JvmStatic
        @Synchronized
        fun getInstance(
            context: Context,
            bleServiceCallback: BleServiceCallback
        ): ZhengChaoBleServiceImp {
            if (instance == null) {
                instance = ZhengChaoBleServiceImp(
                    context.applicationContext
                )
            }
            instance!!.bleServiceCallback = bleServiceCallback

            return instance!!
        }
    }

    private inner class ZuoAnBleDeviceMessageReceivedListener : BleManager.OnReceiveListener {
        override fun onShowMessage(context: Context, intent: Intent) {
            val action: String? = intent.action
            if (BluetoothDevice.ACTION_FOUND == action) {
                val device: BluetoothDevice? =
                    intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
                bleServiceCallback.onSpcDeviceFound(
                    device,
                    BLEDeviceType.ZHENG_CHAO_SPC_DEVICE,
                    null
                )
            } else if (BluetoothLeService.ACTION_GATT_CONNECTED == action) {
                Log.d(TAG, "传感器连接成功")
            } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED == action) {
                Log.d(TAG, "数据传输通道准备就绪,可以发送数据了。")
                bleServiceCallback.onConnected()
            } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED == action) {
                Log.d(TAG, "传感器连接断开")
                val bleService = ble::class.declaredMembers.find {
                    it.name == "mBluetoothLeService"
                }
                bleService?.isAccessible = true
                (bleService?.call() as BluetoothLeService?)?.close()
                bleServiceCallback.onDisconnected()
            } else if (BluetoothLeService.ACTION_DATA_AVAILABLE == action) {
                val data = intent.getByteArrayExtra(BluetoothLeService.EXTRA_DATA)
                when (lastCmd) {
                    CMD_BATTERY -> {
                        val locker: ReentrantLock = singleRunnerLock
                        locker.lock()
                        try {
                            notExecuting.signal()
                        } finally {
                            if (locker.isLocked) {
                                locker.unlock()
                            }
                        }
                        data?.let {
                            val powerDes = String(it)
                            Log.d(TAG, "收到传感器电压数据 $powerDes")
                            val powerInfo = powerDes.split("\r\n")
                            try {
                                bleServiceCallback.onPowerLevelDataReceived(
                                    powerInfo[0].replace(
                                        "%:",
                                        "",
                                        true
                                    ).toFloat() / 100, null
                                )
                                return@let
                            } catch (ex: Exception) {
                                ex.message?.let { it1 -> Log.e(TAG, it1) }
                                Log.d(TAG, "收到非法电压数据, 正确的数据例如%:90 V3.3,表示90%电量,v3.3表示电压3.3")
                            }
                            Log.d(TAG, "收到非法电压数据, 正确的数据例如%:90 V3.3,表示90%电量,v3.3表示电压3.3")
                        }
                    }
                    CMD_TEMPERATURE -> {
                        val locker: ReentrantLock = singleRunnerLock
                        locker.lock()
                        try {
                            notExecuting.signal()
                        } finally {
                            if (locker.isLocked) {
                                locker.unlock()
                            }
                        }
                        data?.let {
                            val temperatureDes = String(it)
                            Log.d(TAG, "收到传感器温度数据$temperatureDes")
                            try {
                                val temperatureInfo = temperatureDes.split("\r\n")[0].replace(
                                    "T:",
                                    "",
                                    true
                                ).toFloat()
                                bleServiceCallback.onTemperatureDataReceived(temperatureInfo)
                            } catch (ex: Exception) {
                                ex.message?.let { it1 -> Log.e(TAG, it1) }
                                Log.d(TAG, "收到非法温度数据, 正确的数据例如T:30.6 e:0.95 表示30.6度 辐射率0.95")
                                spcTaskQueue.clear()
                                bleServiceCallback.onCollectError()
                            }
                        }
                    }
                    CMD_VVEL -> {
                        val locker: ReentrantLock = singleRunnerLock
                        locker.lock()
                        try {
                            notExecuting.signal()
                        } finally {
                            if (locker.isLocked) {
                                locker.unlock()
                            }
                        }
                        data?.let {
                            val vvelDes = String(it)
                            Log.d(TAG, "收到传感器速度数据$vvelDes")
                            try {
                                lastRVEL = vvelDes.split("\r\n")[0].replace(
                                    "v:",
                                    "",
                                    true
                                ).toFloat()
                            } catch (ex: Exception) {
                                ex.message?.let { it1 -> Log.e(TAG, it1) }
                                Log.d(TAG, "收到非法速度数据, 正确的数据例如例如v:0.2 表示速度0.2mm/s")
                                spcTaskQueue.clear()
                                bleServiceCallback.onCollectError()
                            }
                        }
                    }
                    CMD_VAEL -> {
                        val locker: ReentrantLock = singleRunnerLock
                        locker.lock()
                        try {
                            notExecuting.signal()
                        } finally {
                            if (locker.isLocked) {
                                locker.unlock()
                            }
                        }
                        data?.let {
                            val vaelRes = String(it)
                            Log.d(TAG, "收到传感器加速度数据$vaelRes")
                            try {
                                val vaelInfo = vaelRes.split("\r\n")[0].replace(
                                    "a:",
                                    "",
                                    true
                                ).toFloat()
                                bleServiceCallback.onVibAnalysisResultReceived(
                                    mapOf(
                                        Pair(
                                            "RVEL",
                                            lastRVEL!!
                                        ),
                                        Pair(
                                            "PACC",
                                            vaelInfo
                                        )
                                    )
                                )
                            } catch (ex: Exception) {
                                ex.message?.let { it1 -> Log.e(TAG, it1) }
                                Log.d(TAG, "收到非法加速度数据, 正确的加数据例如a:0.2 表示加速度0.2m/s2")
                                spcTaskQueue.clear()
                                bleServiceCallback.onCollectError()
                            }
                        }
                    }
                    CMD_FACTOR_TABLE -> {
                        data?.let { it ->
                            val factorRes = String(it).replace(",\r\n", "")
                            Log.d(
                                TAG,
                                "Thread-${Thread.currentThread().name} 收到传感器系数表数据$factorRes"
                            )
                            try {
                                val factors = factorRes.split(",").map { factorStr ->
                                    factorStr.toFloat()
                                }
                                if (factors.size != 7) {
                                    Log.d(
                                        TAG, "收到非法频谱系数数据, 例如0.111,0.222,0.333,0.444,0.555," +
                                                "0.666 以逗号分隔的6个数值，分别对应低频加速度,低频速度,低频位移,高频加速度" +
                                                ",高频速度,高频位移的频谱系数。采集到的时域谱每个值都需要乘以这个系数。"
                                    )
                                    spcTaskQueue.clear()
                                    bleServiceCallback.onCollectError()
                                    return@let
                                }
                                vibDataFactor = factors.toFloatArray()
                            } catch (ex: Exception) {
                                ex.message?.let { it1 -> Log.e(TAG, it1) }
                                Log.d(
                                    TAG, "收到非法频谱系数数据, 例如0.111,0.222,0.333,0.444,0.555," +
                                            "0.666 以逗号分隔的6个数值，分别对应低频加速度,低频速度,低频位移,高频加速度" +
                                            ",高频速度,高频位移的频谱系数。采集到的时域谱每个值都需要乘以这个系数。"
                                )
                                spcTaskQueue.clear()
                                bleServiceCallback.onCollectError()
                            }
                        }

                        val locker: ReentrantLock = singleRunnerLock
                        locker.lock()
                        try {
                            notExecuting.signal()
                        } finally {
                            if (locker.isLocked) {
                                locker.unlock()
                            }
                        }
                    }
                    CMD_VLB_BLOCK -> {
                        // 时域谱的数据量比较大，所以返回数据是分包回传的，需要自己进行合并，以0xFF 0xFF两个
                        // 字节结尾就表示最后一包数据了，最后2个结尾字节0xFF不是频谱数据要丢弃
                        data?.let {
                            val dataStr = StringBuilder()
                            it.forEach { b ->
                                dataStr.append(b.toInt().toString(16).uppercase() + " ")
                            }
                            Log.d(
                                TAG,
                                "Thread-${Thread.currentThread().name} 收到传感器震动数据:" + dataStr.toString()
                            )
                            vibDataTmp = vibDataTmp.plus(it)
                            val endIndicator: Byte = (0xFF).toByte()
                            if (data.size >= 2
                                && data[data.size - 1] == endIndicator
                                && data[data.size - 2] == endIndicator
                            ) {
                                val locker: ReentrantLock = singleRunnerLock
                                locker.lock()
                                try {
                                    notExecuting.signal()
                                } finally {
                                    if (locker.isLocked) {
                                        locker.unlock()
                                    }
                                }
                                val vibData = ArrayList<Short>()
                                var preByte: Short = 0x0000
                                var byte: Short
                                for (index in 0..vibDataTmp.size - 2) {
                                    byte = vibDataTmp[index].toShort()
                                    if (index % 2 == 0) {
                                        preByte = byte
                                    } else {
                                        val a: Short = (preByte and 0x00ff.toShort())
                                        val b: Short = ((byte * 256) and 0xff00).toShort()
                                        vibData.add((a + b).toShort())
                                    }
                                }
                                dataStr.clear()
                                vibData.forEach { b ->
                                    dataStr.append(b.toInt().toString(16).uppercase() + " ")
                                }
                                Log.d(
                                    TAG,
                                    "震动数据:$dataStr"
                                )

                                val mean =
                                    (vibData.sum().toFloat() / vibData.size).toInt().toShort()
                                var factor: Float?
                                vibDataFactor?.let { factors ->
                                    //  对“读取频谱系数表”返回的数据进行对应获取返回
                                    //  返回值：0.111,0.222,0.333,0.444,0.555,0.666 以逗号分隔的6个数值，
                                    //  分别对应：低频加速度,低频速度,低频位移,高频加速度,高频速度,高频位移的频谱系数。
                                    //  根据频谱采集的命令取对应的频谱系数，采集到的每个频谱时域数据都需要乘以这个系数。
                                    //  例如：如果采集的是低频速度那么Res=0.222;
                                    // double VolCoef=(getRK()*5*1000)/65536.0f;
                                    factor = (if (isHighFrequencyCollection == true) {
                                        factors[3]
                                    } else {
                                        factors[0]
                                    }) * 5 * 1000 / 65536f
                                    Log.d(
                                        TAG,
                                        "接收到所有的振动数据: factor:$factor, mean:$mean"
                                    )

                                    if (sampleRate == null || sample == null) return

                                    val vaelData =
                                        vibData.map { vb -> ((vb - mean) * factor!!) }
                                    val delta = 1f / sampleRate!!
                                    val fs = sampleRate!!
                                    val length = vaelData.size
                                    val vibGzContent =
                                        "${Date().toFormatDate("yyyy-MM-dd'T'HH:mm:ss")}\n$delta\n${fs}\n${length}\ns\nm/s^2\n\n${
                                            vaelData.joinToString("\n")
                                        }"
                                    val result = HashMap<String, Any>()
                                    result["VACC"] = vaelData
                                    Utils.gzip(vibGzContent.toByteArray())?.let { gzipData ->
                                        result["FILE_DATA"] = gzipData
                                        result["FILE_TYPE"] = "ZIPF"
                                    }

                                    bleServiceCallback.onVibDataReceived(result)
                                }
                            }
                        }
                    }
                    else -> data?.let { b ->
                        Log.d(
                            TAG,
                            "接收到传感器数据(Cmd: $lastCmd): ${String(b)}"
                        )
                    }
                }
            }
        }
    }
}

